package com.geek.gkpush.biz.aspect;

import com.geek.gkpush.framework.dispatch.SpringCtxUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * <h1>子节码切面类。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/10/14
 */
@Slf4j
@Order(Integer.MIN_VALUE + 10100)
@Aspect
@Component
public class BizByteCodeAspect {

    // 这里需要去切你需要关注的一些动态热更新的切点
    @Pointcut("(" +
            "execution (* com.geek.gkpush.biz..*FacadeImpl.* (..)) || " +
            "execution (* com.geek.gkpush.framework.redis.listener.AbstractRedisListener.doRecvMsg (..)) || " +
            "execution (* com.geek.gkpush.framework.mq.AbstractMessageListenerConcurrently.consumeMessage (..)) || " +
            "execution (* com.geek.gkpush.framework.job.AbstractLockSimpleJob.doExecute (..)))")
    private void executeService() {
    }

    @Around("executeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 准备一些临时变量
        String className = pjp.getTarget().getClass().getName();
        Method method = ((MethodSignature) pjp.getSignature()).getMethod();
        String methodName = method.getName();

        // 准备热更新需要的 newBeanName，规则为：类名+方法名+Repair
        String newBeanName = String.join("_", className, methodName, "Repair");
        boolean contains = SpringCtxUtils.containsBean(newBeanName);
        log.info("newBeanName: {}, newBeanNameExistsInSpringContainer: {}", newBeanName, contains);

        // 如果 Spring 容器中不包含热更新代码的话，则直接返回
        if (!contains) {
            return pjp.proceed();
        }

        // 能来到这里，说明是需要反射调用热更新的代码了
        Object bean = SpringCtxUtils.getBean(newBeanName);
        Method repairMethod = bean.getClass().getDeclaredMethod(methodName, method.getParameterTypes());
        return repairMethod.invoke(bean, pjp.getArgs());
    }
}